home *** CD-ROM | disk | FTP | other *** search
/ InterCD 2001 May / may_2001.iso / intercd / root / Multimedia / ^DivX_Article / virtualdub / VirtualDub-source-1_4d / AVIOutput.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-03-28  |  32.1 KB  |  1,255 lines

  1. //    VirtualDub - Video processing and capture application
  2. //    Copyright (C) 1998-2001 Avery Lee
  3. //
  4. //    This program is free software; you can redistribute it and/or modify
  5. //    it under the terms of the GNU General Public License as published by
  6. //    the Free Software Foundation; either version 2 of the License, or
  7. //    (at your option) any later version.
  8. //
  9. //    This program is distributed in the hope that it will be useful,
  10. //    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. //    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12. //    GNU General Public License for more details.
  13. //
  14. //    You should have received a copy of the GNU General Public License
  15. //    along with this program; if not, write to the Free Software
  16. //    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17.  
  18. #include "VirtualDub.h"
  19.  
  20. #include "AudioSource.h"
  21. #include "VideoSource.h"
  22. #include "AVIIndex.h"
  23. #include "FastWriteStream.h"
  24.  
  25. #include "wrappedMMIO.h"
  26. #include "Error.h"
  27. #include "AVIOutput.h"
  28. #include "oshelper.h"
  29.  
  30. ///////////////////////////////////////////
  31.  
  32. extern "C" unsigned long version_num;
  33.  
  34. ///////////////////////////////////////////
  35.  
  36. typedef __int64 QUADWORD;
  37.  
  38. // The following comes from the OpenDML 1.0 spec for extended AVI files
  39.  
  40. // bIndexType codes
  41. //
  42. #define AVI_INDEX_OF_INDEXES 0x00    // when each entry in aIndex
  43.                                     // array points to an index chunk
  44.  
  45. #define AVI_INDEX_OF_CHUNKS 0x01    // when each entry in aIndex
  46.                                     // array points to a chunk in the file
  47.  
  48. #define AVI_INDEX_IS_DATA    0x80    // when each entry is aIndex is
  49.                                     // really the data
  50.  
  51. // bIndexSubtype codes for INDEX_OF_CHUNKS
  52.  
  53. #define AVI_INDEX_2FIELD    0x01    // when fields within frames
  54.                                     // are also indexed
  55.     struct _avisuperindex_entry {
  56.         QUADWORD qwOffset;        // absolute file offset, offset 0 is
  57.                                 // unused entry??
  58.         DWORD dwSize;            // size of index chunk at this offset
  59.         DWORD dwDuration;        // time span in stream ticks
  60.     };
  61.     struct _avistdindex_entry {
  62.         DWORD dwOffset;            // qwBaseOffset + this is absolute file offset
  63.         DWORD dwSize;            // bit 31 is set if this is NOT a keyframe
  64.     };
  65.  
  66. #pragma pack(push)
  67. #pragma pack(2)
  68.  
  69. typedef struct _avisuperindex_chunk {
  70.     FOURCC fcc;                    // Æix##Æ
  71.     DWORD cb;                    // size of this structure
  72.     WORD wLongsPerEntry;        // must be 4 (size of each entry in aIndex array)
  73.     BYTE bIndexSubType;            // must be 0 or AVI_INDEX_2FIELD
  74.     BYTE bIndexType;            // must be AVI_INDEX_OF_INDEXES
  75.     DWORD nEntriesInUse;        // number of entries in aIndex array that
  76.                                 // are used
  77.     DWORD dwChunkId;            // Æ##dcÆ or Æ##dbÆ or Æ##wbÆ, etc
  78.     DWORD dwReserved[3];        // must be 0
  79.     struct _avisuperindex_entry aIndex[];
  80. } AVISUPERINDEX, * PAVISUPERINDEX;
  81.  
  82. typedef struct _avistdindex_chunk {
  83.     FOURCC fcc;                    // Æix##Æ
  84.     DWORD cb;
  85.     WORD wLongsPerEntry;        // must be sizeof(aIndex[0])/sizeof(DWORD)
  86.     BYTE bIndexSubType;            // must be 0
  87.     BYTE bIndexType;            // must be AVI_INDEX_OF_CHUNKS
  88.     DWORD nEntriesInUse;        //
  89.     DWORD dwChunkId;            // Æ##dcÆ or Æ##dbÆ or Æ##wbÆ etc..
  90.     QUADWORD qwBaseOffset;        // all dwOffsets in aIndex array are
  91.                                 // relative to this
  92.     DWORD dwReserved3;            // must be 0
  93.     struct _avistdindex_entry aIndex[];
  94. } AVISTDINDEX, * PAVISTDINDEX;
  95.  
  96. #pragma pack(pop)
  97.  
  98. ///////////////////////////////////////////
  99.  
  100. AVIOutputStream::AVIOutputStream(class AVIOutput *output) {
  101.     this->output = output;
  102.     format = NULL;
  103. }
  104.  
  105. AVIOutputStream::~AVIOutputStream() {
  106.     delete format;
  107. }
  108.  
  109. BOOL AVIOutputStream::_write(FOURCC ckid, LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer) {
  110.     output->writeIndexedChunk(ckid, dwIndexFlags, lpBuffer, cbBuffer);
  111. //    output->writeIndexedChunk('bd00', dwIndexFlags, lpBuffer, cbBuffer);
  112.     return TRUE;
  113. }
  114.  
  115. BOOL AVIOutputStream::finalize() {
  116.     _RPT0(0,"AVIOutputStream: finalize()\n");
  117.     return TRUE;
  118. }
  119.  
  120. ////////////////////////////////////
  121.  
  122. AVIAudioOutputStream::AVIAudioOutputStream(class AVIOutput *out) : AVIOutputStream(out) {
  123.     lTotalSamplesWritten = 0;
  124. }
  125.  
  126. BOOL AVIAudioOutputStream::write(LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer, LONG lSamples) {
  127.     BOOL success;
  128.  
  129.     success = _write(mmioFOURCC('0','1','w','b'), dwIndexFlags, lpBuffer, cbBuffer);
  130.  
  131.     if (success) lTotalSamplesWritten += lSamples;
  132.  
  133.     return success;
  134. }
  135.  
  136. BOOL AVIAudioOutputStream::finalize() {
  137.     _RPT0(0,"AVIAudioOutputStream: finalize()\n");
  138.  
  139.     if (!lTotalSamplesWritten)
  140.         streamInfo.dwLength = 1;
  141.     else
  142.         streamInfo.dwLength = lTotalSamplesWritten;
  143.     return TRUE;
  144. }
  145.  
  146. BOOL AVIAudioOutputStream::flush() {
  147.     return TRUE;
  148. }
  149.  
  150. ////////////////////////////////////
  151.  
  152. AVIVideoOutputStream::AVIVideoOutputStream(class AVIOutput *out) : AVIOutputStream(out) {
  153.     lTotalSamplesWritten = 0;
  154. }
  155.  
  156. BOOL AVIVideoOutputStream::write(LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer, LONG lSamples) {
  157.     BOOL success;
  158.  
  159.     success = _write(id, dwIndexFlags, lpBuffer, cbBuffer);
  160.  
  161.     if (success) lTotalSamplesWritten += lSamples;
  162.  
  163.     return success;
  164. }
  165.  
  166. BOOL AVIVideoOutputStream::finalize() {
  167.     _RPT0(0,"AVIVideoOutputStream: finalize()\n");
  168.  
  169.     if (!lTotalSamplesWritten)
  170.         streamInfo.dwLength = 1;
  171.     else
  172.         streamInfo.dwLength = lTotalSamplesWritten;
  173.     return TRUE;
  174. }
  175.  
  176. ////////////////////////////////////
  177.  
  178. char AVIOutput::szME[]="AVIOutput";
  179.  
  180. AVIOutput::AVIOutput() {
  181.     audioOut            = NULL;
  182.     videoOut            = NULL;
  183. }
  184.  
  185. AVIOutput::~AVIOutput() {
  186.     _RPT0(0,"AVIOutput::~AVIOutput()\n");
  187.     delete audioOut;
  188.     delete videoOut;
  189. }
  190.  
  191. AVIOutputFile::AVIOutputFile() {
  192.     hFile                = NULL;
  193.     fastIO                = NULL;
  194.     index                = NULL;
  195.     index_audio            = NULL;
  196.     index_video            = NULL;
  197.     fCaching            = TRUE;
  198.     i64FilePosition        = 0;
  199.     i64XBufferLevel        = 0;
  200.     xblock                = 0;
  201.     fExtendedAVI        = true;
  202.     lAVILimit            = 0x7F000000L;
  203.     fCaptureMode        = false;
  204.     iPadOffset            = 0;
  205.     pSegmentHint        = NULL;
  206.     cbSegmentHint        = 0;
  207.     fInitComplete        = false;
  208.  
  209.     pHeaderBlock        = (char *)allocmem(65536);
  210.     nHeaderLen            = 0;
  211.     i64FarthestWritePoint    = 0;
  212.     lLargestIndexDelta[0]    = 0;
  213.     lLargestIndexDelta[1]    = 0;
  214.     i64FirstIndexedChunk[0] = 0;
  215.     i64FirstIndexedChunk[1] = 0;
  216.     i64LastIndexedChunk[0] = 0;
  217.     i64LastIndexedChunk[1] = 0;
  218.     lIndexedChunkCount[0]    = 0;
  219.     lIndexedChunkCount[1]    = 0;
  220.     lIndexSize            = 0;
  221.     fPreemptiveExtendFailed = false;
  222. }
  223.  
  224. AVIOutputFile::~AVIOutputFile() {
  225.     delete index;
  226.     delete index_audio;
  227.     delete index_video;
  228.     delete pSegmentHint;
  229.     freemem(pHeaderBlock);
  230.  
  231.     _RPT0(0,"AVIOutputFile: destructor called\n");
  232.  
  233.     if (hFile) {
  234.         LONG lHi = (LONG)(i64FarthestWritePoint>>32);
  235.         DWORD dwError;
  236.  
  237.         if (0xFFFFFFFF != SetFilePointer(hFile, (LONG)i64FarthestWritePoint, &lHi, FILE_BEGIN)
  238.             || (dwError = GetLastError()) != NO_ERROR) {
  239.  
  240.             SetEndOfFile(hFile);
  241.         }
  242.     }
  243.  
  244.     delete fastIO;
  245.  
  246.     if (hFile)
  247.         CloseHandle(hFile);
  248. }
  249.  
  250. //////////////////////////////////
  251.  
  252. BOOL AVIOutputFile::initOutputStreams() {
  253.     if (!(audioOut = new AVIAudioOutputStream(this))) return FALSE;
  254.     if (!(videoOut = new AVIVideoOutputStream(this))) return FALSE;
  255.  
  256.     return TRUE;
  257. }
  258.  
  259. void AVIOutputFile::disable_os_caching() {
  260.     fCaching = FALSE;
  261.     lChunkSize = 0;
  262. }
  263.  
  264. void AVIOutputFile::disable_extended_avi() {
  265.     fExtendedAVI = false;
  266. }
  267.  
  268. void AVIOutputFile::set_1Gb_limit() {
  269.     lAVILimit = 0x3F000000L;
  270. }
  271.  
  272. void AVIOutputFile::set_chunk_size(long l) {
  273.     lChunkSize = l;
  274. }
  275.  
  276. void AVIOutputFile::set_capture_mode(bool b) {
  277.     fCaptureMode = b;
  278. }
  279.  
  280. void AVIOutputFile::setSegmentHintBlock(bool fIsFinal, const char *pszNextPath, int cbBlock) {
  281.     if (!pSegmentHint)
  282.         if (!(pSegmentHint = new char[cbBlock]))
  283.             throw MyMemoryError();
  284.  
  285.     cbSegmentHint = cbBlock;
  286.  
  287.     memset(pSegmentHint, 0, cbBlock);
  288.  
  289.     pSegmentHint[0] = !fIsFinal;
  290.     if (pszNextPath)
  291.         strcpy(pSegmentHint+1, pszNextPath);
  292. }
  293.  
  294. // I don't like to bitch about other programs (well, okay, so I do), but
  295. // Windows Media Player deserves special attention here.  The ActiveMovie
  296. // implementation of OpenDML hierarchial indexing >2Gb *SUCKS*.  It can't
  297. // cope with a JUNK chunk at the end of the hdrl chunk (even though
  298. // the Microsoft documentation in AVIRIFF.H shows one), requires that
  299. // all standard indexes be the same size except for the last one, and
  300. // requires buffer size information for streams.  NONE of this is required
  301. // by ActiveMovie when an extended index is absent (easily verified by
  302. // changing the 'indx' chunks to JUNK chunks).  While diagnosing these
  303. // problems I got an interesting array of error messages from WMP,
  304. // including:
  305. //
  306. //    o Downloading codec from activex.microsoft.com
  307. //        (Because of an extended index!?)
  308. //    o "Cannot allocate memory because no size has been set"
  309. //        ???
  310. //    o "The file format is invalid."
  311. //        Detail: "The file format is invalid. (Error=8004022F)"
  312. //        Gee, that clears everything up.
  313. //    o My personal favorite: recursion of the above until the screen
  314. //        has 100+ dialogs and WMP crashes with a stack fault.
  315. //
  316. // Basically, supporting WMP (or as I like to call it, WiMP) was an
  317. // absolute 100% pain in the ass.
  318.  
  319. BOOL AVIOutputFile::init(const char *szFile, LONG xSize, LONG ySize, BOOL videoIn, BOOL audioIn, LONG bufferSize, BOOL is_interleaved) {
  320.     return _init(szFile, xSize, ySize, videoIn, audioIn, bufferSize, is_interleaved, true);
  321. }
  322.  
  323. FastWriteStream *AVIOutputFile::initCapture(const char *szFile, LONG xSize, LONG ySize, BOOL videoIn, BOOL audioIn, LONG bufferSize, BOOL is_interleaved) {
  324.     return _init(szFile, xSize, ySize, videoIn, audioIn, bufferSize, is_interleaved, false)
  325.         ? fastIO : NULL;
  326. }
  327.  
  328. BOOL AVIOutputFile::_init(const char *szFile, LONG xSize, LONG ySize, BOOL videoIn, BOOL audioIn, LONG bufferSize, BOOL is_interleaved, bool fThreaded) {
  329.     AVISUPERINDEX asi;
  330.     struct _avisuperindex_entry asie_dumb[MAX_SUPERINDEX_ENTRIES];
  331.  
  332.     fLimitTo4Gb = IsFilenameOnFATVolume(szFile);
  333.  
  334.     if (audioIn) {
  335.         if (!audioOut) return FALSE;
  336.     } else {
  337.         delete audioOut;
  338.         audioOut = NULL;
  339.     }
  340.  
  341.     if (!videoOut) return FALSE;
  342.  
  343.     // Allocate indexes
  344.  
  345.     if (!(index = new AVIIndex())) return FALSE;
  346.  
  347.     if (fExtendedAVI) {
  348.         if (!(index_audio = new AVIIndex())) return FALSE;
  349.         if (!(index_video = new AVIIndex())) return FALSE;
  350.     }
  351.  
  352.     // Initialize main AVI header (avih)
  353.  
  354.     memset(&avihdr, 0, sizeof avihdr);
  355.     avihdr.dwMicroSecPerFrame        = MulDiv(videoOut->streamInfo.dwScale, 1000000L, videoOut->streamInfo.dwRate);
  356.     avihdr.dwMaxBytesPerSec            = 0;
  357.     avihdr.dwPaddingGranularity        = 0;
  358.     avihdr.dwFlags                    = AVIF_HASINDEX | (is_interleaved ? AVIF_ISINTERLEAVED : 0);
  359.     avihdr.dwTotalFrames            = videoOut->streamInfo.dwLength;
  360.     avihdr.dwInitialFrames            = 0;
  361.     avihdr.dwStreams                = audioIn ? 2 : 1;
  362.     avihdr.dwSuggestedBufferSize    = 0;
  363.     avihdr.dwWidth                    = xSize;
  364.     avihdr.dwHeight                    = ySize;
  365.  
  366.     // Initialize file
  367.  
  368.     if (!fCaching) {
  369.  
  370.         hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN, NULL);
  371.  
  372.         if (INVALID_HANDLE_VALUE == hFile)
  373.             throw MyWin32Error("%s: %%s", GetLastError(), szME);
  374.  
  375.         if (!(fastIO = new FastWriteStream(szFile, bufferSize, lChunkSize ? lChunkSize : bufferSize/2, fThreaded)))
  376.             throw MyMemoryError();
  377.     } else {
  378.         hFile = CreateFile(szFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | FILE_FLAG_WRITE_THROUGH, NULL);
  379.  
  380.         if (INVALID_HANDLE_VALUE == hFile)
  381.             throw MyWin32Error("%s: %%s", GetLastError(), szME);
  382.     }
  383.  
  384.     i64FilePosition = 0;
  385.  
  386.     ////////// Initialize the first 'AVI ' chunk //////////
  387.  
  388.     __int64 hdrl_pos;
  389.     __int64 odml_pos;
  390.  
  391.     DWORD dw[64];
  392.  
  393.     // start RIFF chunk
  394.  
  395.     dw[0]    = FOURCC_RIFF;
  396.     dw[1]    = 0;
  397.     dw[2]    = formtypeAVI;
  398.  
  399.     _writeHdr(dw, 12);
  400.  
  401.     // start header chunk
  402.  
  403.     hdrl_pos = _beginList(listtypeAVIHEADER);
  404.  
  405.     // write out main AVI header
  406.  
  407.     main_hdr_pos = _writeHdrChunk(ckidAVIMAINHDR, &avihdr, sizeof avihdr);
  408.  
  409.     _RPT1(0,"Main header is at %08lx\n", main_hdr_pos);
  410.  
  411.     // start video stream headers
  412.  
  413.     strl_pos = _beginList(listtypeSTREAMHEADER);
  414.  
  415.     // write out video stream header and format
  416.  
  417.     video_hdr_pos    = _writeHdrChunk(ckidSTREAMHEADER, &videoOut->streamInfo, sizeof videoOut->streamInfo);
  418.     _writeHdrChunk(ckidSTREAMFORMAT, videoOut->getFormat(), videoOut->getFormatLen());
  419.  
  420.     _RPT1(0,"Video header is at %08lx\n", video_hdr_pos);
  421.  
  422.     // write out video superindex (but make it a JUNK chunk for now).
  423.  
  424.     if (fExtendedAVI) {
  425.         memset(asie_dumb, 0, sizeof asie_dumb);
  426.         video_indx_pos = _getPosition();
  427.         asi.fcc = ckidAVIPADDING;
  428.         asi.cb = (sizeof asi)-8 + MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry);
  429.         _writeHdr(&asi, sizeof asi);
  430.         _writeHdr(asie_dumb, MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry));
  431.     }
  432.  
  433.     // finish video stream header
  434.  
  435.     _closeList(strl_pos);
  436.  
  437.     videoOut->streamInfo.dwSuggestedBufferSize = 0;
  438.  
  439.     // if there is audio...
  440.  
  441.     if (audioIn) {
  442.         // start audio stream headers
  443.  
  444.         strl_pos = _beginList(listtypeSTREAMHEADER);
  445.  
  446.         // write out audio stream header and format
  447.  
  448.         audio_hdr_pos    = _writeHdrChunk(ckidSTREAMHEADER, &audioOut->streamInfo, sizeof audioOut->streamInfo);
  449.         audio_format_pos = _writeHdrChunk(ckidSTREAMFORMAT, audioOut->getFormat(), audioOut->getFormatLen());
  450.  
  451.         _RPT1(0,"Audio header is at %08lx\n", audio_hdr_pos);
  452.  
  453.         // write out audio superindex (but make it a JUNK chunk for now).
  454.  
  455.         if (fExtendedAVI) {
  456.             audio_indx_pos = _getPosition();
  457.             asi.fcc = ckidAVIPADDING;
  458.             asi.cb = (sizeof asi)-8 + MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry);
  459.             _writeHdr(&asi, sizeof asi);
  460.             _writeHdr(asie_dumb, MAX_SUPERINDEX_ENTRIES*sizeof(_avisuperindex_entry));
  461.         }
  462.  
  463.         // finish audio stream header
  464.  
  465.         _closeList(strl_pos);
  466.  
  467.         audioOut->streamInfo.dwSuggestedBufferSize = 0;
  468.     }
  469.  
  470.     // write out dmlh header (indicates real # of frames)
  471.  
  472.     if (fExtendedAVI) {
  473.         odml_pos = _beginList('lmdo');
  474.  
  475.         memset(dw, 0, sizeof dw);
  476.         dmlh_pos = _writeHdrChunk('hlmd', dw, 62*4);
  477.  
  478.         _closeList(odml_pos);
  479.     }
  480.  
  481.     // write out segment hint block
  482.  
  483.     if (pSegmentHint)
  484.         seghint_pos = _writeHdrChunk('mges', pSegmentHint, cbSegmentHint);
  485.  
  486.     _closeList(hdrl_pos);
  487. //    _flushHdr();
  488.  
  489.     // pad out to a multiple of 2048 bytes
  490.     //
  491.     // WARNING: ActiveMovie/WMP can't handle a trailing JUNK chunk in hdrl
  492.     //            if an extended index is in use.  It says the file format
  493.     //            is invalid!
  494.  
  495.     {
  496.         char *s;
  497.         long pad;
  498.  
  499.         pad = (2048 - ((_getPosition()+8)&2047))&2047;
  500.  
  501.         if (pad) {
  502.             if (!(s = (char *)allocmem(pad)))
  503.                 return FALSE;
  504.  
  505.             memset(s,0,pad);
  506.  
  507.             if (pad > 80)
  508.                 sprintf(s, "VirtualDub build %d/%s", version_num,
  509. #ifdef _DEBUG
  510.         "debug"
  511. #else
  512.         "release"
  513. #endif
  514.                 );
  515.  
  516.             _writeHdrChunk(ckidAVIPADDING, s, pad);
  517.  
  518.             freemem(s);
  519.         }
  520.  
  521. //        // If we are using a fast path, sync the fast path to the slow path
  522.  
  523. //        if (fastIO)
  524. //            fastIO->Seek(i64FilePosition);
  525.     }
  526.  
  527.     if (fastIO)
  528. //        fastIO->FlushStart();
  529.         fastIO->Put(pHeaderBlock, nHeaderLen);
  530.     else
  531.         _flushHdr();
  532.  
  533.     // If we're using the fast path, we're aligned to a sector boundary.
  534.     // Write out the 12 header bytes.
  535.  
  536.     _openXblock();
  537.  
  538.  
  539.     {
  540.         DWORD dwLo, dwHi;
  541.  
  542.         dwLo = GetFileSize(hFile, &dwHi);
  543.  
  544.         if (dwLo != 0xFFFFFFFF || GetLastError()==NO_ERROR)
  545.             i64EndOfFile = dwLo | ((__int64)dwHi << 32);
  546.         else
  547.             i64EndOfFile = 0;
  548.     }
  549.  
  550.     fInitComplete = true;
  551.  
  552.     return TRUE;
  553. }
  554.  
  555. BOOL AVIOutputFile::finalize() {
  556.     AVISUPERINDEX asi_video;
  557.     AVISUPERINDEX asi_audio;
  558.     struct _avisuperindex_entry asie_video[MAX_SUPERINDEX_ENTRIES];
  559.     struct _avisuperindex_entry asie_audio[MAX_SUPERINDEX_ENTRIES];
  560.     DWORD dw;
  561.     int i;
  562.  
  563.     if (!fInitComplete)
  564.         return TRUE;
  565.  
  566.     if (videoOut) if (!videoOut->finalize()) return FALSE;
  567.     if (audioOut) if (!audioOut->finalize()) return FALSE;
  568.  
  569.     // fast path: clean it up and resync slow path.
  570.  
  571.     // create extended indices
  572.  
  573.     if (fExtendedAVI && xblock != 0) {
  574.         _createNewIndices(index_video, &asi_video, asie_video, false);
  575.         if (audioOut)
  576.             _createNewIndices(index_audio, &asi_audio, asie_audio, true);
  577.     }
  578.  
  579.     // finish last Xblock
  580.  
  581.     _closeXblock();
  582.  
  583.     if (fastIO) {
  584.         char pad[2048+8];
  585.  
  586.         // pad to next boundary
  587.  
  588.         _RPT1(0,"AVIOutputFile: starting pad at position %08I64x\n", i64FilePosition);
  589.  
  590.         *(long *)(pad + 0) = 'KNUJ';
  591.         *(long *)(pad + 4) = (2048 - ((i64FilePosition+8)&2047))&2047;
  592.         memset(pad+8, 0, 2048);
  593.         _write(pad, *(long *)(pad+4) + 8);
  594.  
  595.         // flush fast path, get disk position
  596.  
  597.         fastIO->Flush1();
  598.         fastIO->Flush2(hFile);
  599.  
  600.         // seek slow path up
  601.  
  602.         _seekDirect(i64FilePosition);
  603.  
  604.     }    
  605.  
  606.     // truncate file
  607.  
  608.     SetEndOfFile(hFile);
  609.  
  610.     _RPT0(0,"AVIOutputFile: Writing main AVI header...\n");
  611.     _seekHdr(main_hdr_pos+8);
  612.     _writeHdr(&avihdr, sizeof avihdr);
  613.  
  614.     _RPT0(0,"AVIOutputFile: Writing video header...\n");
  615.     _seekHdr(video_hdr_pos+8);
  616.     _writeHdr(&videoOut->streamInfo, sizeof(AVIStreamHeader_fixed));
  617.  
  618.     if (audioOut) {
  619.         _RPT0(0,"AVIOutputFile: Writing audio header...\n");
  620.         _seekHdr(audio_hdr_pos+8);
  621.         _writeHdr(&audioOut->streamInfo, sizeof(AVIStreamHeader_fixed));
  622.  
  623.         // we have to rewrite the audio format, in case someone
  624.         // fixed fields in the format afterward (MPEG-1/L3)
  625.  
  626.         _RPT0(0,"AVIOutputFile: Writing audio format...\n");
  627.  
  628.         _seekHdr(audio_format_pos+8);
  629.         _writeHdr(audioOut->getFormat(), audioOut->getFormatLen());
  630.     }
  631.  
  632.     if (fExtendedAVI) {
  633.         _RPT0(0,"AVIOutputFile: writing dmlh header...\n");
  634.         _seekHdr(dmlh_pos+8);
  635.         dw = videoOut->streamInfo.dwLength;
  636.         _writeHdr(&dw, 4);
  637.  
  638.         if (xblock > 1) {
  639.             _RPT0(0,"AVIOutputFile: writing video superindex...\n");
  640.  
  641.             _seekHdr(video_indx_pos);
  642.             _writeHdr(&asi_video, sizeof asi_video);
  643.             _writeHdr(asie_video, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES);
  644.  
  645.             if (audioOut) {
  646.                 _seekHdr(audio_indx_pos);
  647.                 _writeHdr(&asi_audio, sizeof asi_audio);
  648.                 _writeHdr(asie_audio, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES);
  649.             }
  650.         }
  651.     }
  652.  
  653.     if (pSegmentHint) {
  654.         _seekHdr(seghint_pos+8);
  655.         _writeHdr(pSegmentHint, cbSegmentHint);
  656.     }
  657.  
  658.     _flushHdr();
  659.  
  660.     _RPT0(0,"AVIOutputFile: closing RIFF and movi chunks...\n");
  661.  
  662.     for(i=0; i<xblock; i++) {
  663.         DWORD dwLen;
  664.         
  665.         dwLen = (DWORD)avi_riff_len[i];
  666.         _seekDirect(avi_riff_pos[i]+4);
  667.         _writeDirect(&dwLen, 4);
  668.  
  669.         dwLen = (DWORD)avi_movi_len[i];
  670.         _seekDirect(avi_movi_pos[i]+4);
  671.         _writeDirect(&dwLen, 4);
  672.     }
  673.  
  674. //    if (!FlushFileBuffers(hFile))
  675. //        throw MyWin32Error("%s: %%s", GetLastError(), szME);
  676.  
  677.     // What do you do when a close fails?
  678.  
  679.     if (!CloseHandle(hFile)) {
  680.         hFile = NULL;
  681.         throw MyWin32Error("%s: %%s", GetLastError(), szME);
  682.     }
  683.  
  684.     hFile = NULL;
  685.  
  686.     return TRUE;
  687. }
  688.  
  689. BOOL AVIOutputFile::isPreview() { return FALSE; }
  690.  
  691. long AVIOutputFile::bufferStatus(long *lplBufferSize) {
  692.     if (fastIO) {
  693.         return fastIO->getBufferStatus(lplBufferSize);
  694.     } else {
  695.         return 0;
  696.     }
  697. }
  698.  
  699. ////////////////////////////
  700.  
  701. __int64 AVIOutputFile::_writeHdr(void *data, long len) {
  702.     if (nHeaderLen < (long)i64FilePosition + len)
  703.         nHeaderLen = (long)i64FilePosition + len;
  704.  
  705.     memcpy(pHeaderBlock + (long)i64FilePosition, data, len);
  706.  
  707.     i64FilePosition += len;
  708.  
  709.     return i64FilePosition - len;
  710. }
  711.  
  712. __int64 AVIOutputFile::_beginList(FOURCC ckid) {
  713.     DWORD dw[3];
  714.  
  715.     dw[0] = FOURCC_LIST;
  716.     dw[1] = 0;
  717.     dw[2] = ckid;
  718.  
  719.     return _writeHdr(dw, 12);
  720. }
  721.  
  722. __int64 AVIOutputFile::_writeHdrChunk(FOURCC ckid, void *data, long len) {
  723.     DWORD dw[2];
  724.     __int64 pos;
  725.  
  726.     dw[0] = ckid;
  727.     dw[1] = len;
  728.  
  729.     pos = _writeHdr(dw, 8);
  730.  
  731.     _writeHdr(data, len);
  732.  
  733.     if (len & 1) {
  734.         dw[0] = 0;
  735.  
  736.         _writeHdr(dw, 1);
  737.     }
  738.  
  739.     return pos;
  740. }
  741.  
  742. void AVIOutputFile::_closeList(__int64 pos) {
  743.     DWORD dw;
  744.     __int64 i64FPSave = i64FilePosition;
  745.     
  746.     dw = i64FilePosition - (pos+8);
  747.  
  748.     _seekHdr(pos+4);
  749.     _writeHdr(&dw, 4);
  750.     _seekHdr(i64FPSave);
  751. }
  752.  
  753. void AVIOutputFile::_flushHdr() {
  754.     DWORD dwActual;
  755.     DWORD dwError;
  756.  
  757.     if (0xFFFFFFFF == SetFilePointer(hFile, 0, NULL, FILE_BEGIN))
  758.         if ((dwError = GetLastError()) != NO_ERROR)
  759.             throw MyWin32Error("%s: %%s", dwError, szME);
  760.  
  761.     i64FilePosition = 0;
  762.  
  763.     if (!WriteFile(hFile, pHeaderBlock, nHeaderLen, &dwActual, NULL)
  764.         || dwActual != nHeaderLen)
  765.  
  766.         throw MyWin32Error("%s: %%s", GetLastError(), szME);
  767.  
  768.     i64FilePosition = nHeaderLen;
  769.  
  770.     if (i64FilePosition > i64FarthestWritePoint)
  771.         i64FarthestWritePoint = i64FilePosition;
  772. }
  773.  
  774. __int64 AVIOutputFile::_getPosition() {
  775.     return i64FilePosition;
  776. }
  777.  
  778. void AVIOutputFile::_seekHdr(__int64 i64NewPos) {
  779.     i64FilePosition = i64NewPos;
  780. }
  781.  
  782. void AVIOutputFile::_seekDirect(__int64 i64NewPos) {
  783.     LONG lHi = (LONG)(i64NewPos>>32);
  784.     DWORD dwError;
  785.  
  786. //    _RPT1(0,"Seeking to %I64d\n", i64NewPos);
  787.  
  788.     if (0xFFFFFFFF == SetFilePointer(hFile, (LONG)i64NewPos, &lHi, FILE_BEGIN))
  789.         if ((dwError = GetLastError()) != NO_ERROR)
  790.             throw MyWin32Error("%s: %%s", dwError, szME);
  791.  
  792.     i64FilePosition = i64NewPos;
  793. }
  794.  
  795. __int64 AVIOutputFile::_writeDirect(void *data, long len) {
  796.     DWORD dwActual;
  797.  
  798.     if (!WriteFile(hFile, data, len, &dwActual, NULL)
  799.         || dwActual != len)
  800.  
  801.         throw MyWin32Error("%s: %%s", GetLastError(), szME);
  802.  
  803.     i64FilePosition += len;
  804.  
  805.     if (i64FilePosition > i64FarthestWritePoint)
  806.         i64FarthestWritePoint = i64FilePosition;
  807.  
  808.     return i64FilePosition - len;
  809. }
  810.  
  811. bool AVIOutputFile::_extendFile(__int64 i64NewPoint) {
  812.     bool fSuccess;
  813.  
  814.     // Have we already extended the file past that point?
  815.  
  816.     if (i64NewPoint < i64EndOfFile)
  817.         return true;
  818.  
  819.     // Attempt to extend the file.
  820.  
  821.     __int64 i64Save = i64FilePosition;
  822.  
  823.     _seekDirect(i64NewPoint);
  824.     fSuccess = !!SetEndOfFile(hFile);
  825.     _seekDirect(i64Save);
  826.  
  827.     if (fSuccess) {
  828.         i64EndOfFile = i64NewPoint;
  829. //        _RPT1(0,"Successfully extended file to %I64d bytes\n", i64EndOfFile);
  830.     } else {
  831. //        _RPT1(0,"Failed to extend file to %I64d bytes\n", i64NewPoint);
  832.     }
  833.  
  834.     return fSuccess;
  835. }
  836.  
  837. void AVIOutputFile::_write(void *data, int len) {
  838.  
  839.     if (!fPreemptiveExtendFailed && i64FilePosition + len + lIndexSize > i64EndOfFile - 8388608) {
  840.         fPreemptiveExtendFailed = !_extendFile((i64FilePosition + len + lIndexSize + 16777215) & -8388608);
  841.     }
  842.  
  843.     if (fastIO) {
  844.         fastIO->Put(data,len);
  845.         i64FilePosition += len;
  846.         if (i64FilePosition > i64FarthestWritePoint)
  847.             i64FarthestWritePoint = i64FilePosition;
  848.     } else
  849. //        _writeHdr(data,len);
  850.         _writeDirect(data, len);
  851. }
  852.  
  853. void AVIOutputFile::writeIndexedChunk(FOURCC ckid, LONG dwIndexFlags, LPVOID lpBuffer, LONG cbBuffer) {
  854.     AVIIndexEntry2 avie;
  855.     long buf[5];
  856.     static char zero = 0;
  857.     long siz;
  858.     bool fOpenNewBlock = false;
  859.     int nStream = 0;
  860.  
  861.     if ((ckid&0xffff) > (int)'00')
  862.         nStream = 1;
  863.  
  864.     // Determine if we need to open another RIFF block (xblock).
  865.  
  866.     siz = cbBuffer + (cbBuffer&1) + 16;
  867.  
  868.     // The original AVI format can't accommodate RIFF chunks >4Gb due to the
  869.     // use of 32-bit size fields.  Most RIFF parsers don't handle >2Gb because
  870.     // of inappropriate use of signed variables.  And to top it all off,
  871.     // stupid mistakes in the MCI RIFF parser prevent processing beyond the
  872.     // 1Gb mark.
  873.     //
  874.     // To be save, we keep the first RIFF AVI chunk below 1Gb, and subsequent
  875.     // RIFF AVIX chunks below 2Gb.  We have to leave ourselves a little safety
  876.     // margin (16Mb in this case) for index blocks.
  877.  
  878.     if (fExtendedAVI)
  879.         if (i64XBufferLevel + siz > (xblock ? 0x7F000000 : lAVILimit))
  880.             fOpenNewBlock = true;
  881.  
  882.     // Check available disk space.
  883.     //
  884.     // Take the largest separation between data blocks,
  885.  
  886.     __int64 chunkloc;
  887.     int idxblocksize;
  888.     int idxblocks;
  889.     __int64 maxpoint;
  890.  
  891.     chunkloc = i64FilePosition;
  892.     if (fOpenNewBlock)
  893.         chunkloc += 24;
  894.  
  895.     if (!i64FirstIndexedChunk[nStream])
  896.         i64FirstIndexedChunk[nStream] = chunkloc;
  897.  
  898.     if ((long)(chunkloc - i64LastIndexedChunk[nStream]) > lLargestIndexDelta[nStream])
  899.         lLargestIndexDelta[nStream] = (long)(chunkloc - i64LastIndexedChunk[nStream]);
  900.  
  901.     ++lIndexedChunkCount[nStream];
  902.  
  903.     // compute how much total space we need to close the file
  904.  
  905.     idxblocks = 0;
  906.  
  907.     if (lLargestIndexDelta[0]) {
  908.         idxblocksize = (int)(0x100000000i64 / lLargestIndexDelta[0]);
  909.         if (idxblocksize > MAX_INDEX_ENTRIES)
  910.             idxblocksize = MAX_INDEX_ENTRIES;
  911.         idxblocks = (lIndexedChunkCount[0] + idxblocksize - 1) / idxblocksize;
  912.     }
  913.     if (lLargestIndexDelta[1]) {
  914.         idxblocksize = (int)(0x100000000i64 / lLargestIndexDelta[1]);
  915.         if (idxblocksize > MAX_INDEX_ENTRIES)
  916.             idxblocksize = MAX_INDEX_ENTRIES;
  917.         idxblocks += (lIndexedChunkCount[1] + idxblocksize - 1) / idxblocksize;
  918.     }
  919.  
  920.     lIndexSize = 0;
  921.  
  922.     if (fExtendedAVI)
  923.         lIndexSize = idxblocks*sizeof(AVISTDINDEX);
  924.  
  925.     lIndexSize += 8 + 16*(lIndexedChunkCount[0]+lIndexedChunkCount[1]);
  926.     
  927.     // Give ourselves ~4K of headroom...
  928.  
  929.     maxpoint = (chunkloc + cbBuffer + 1 + 8 + 14 + 2047 + lIndexSize + 4096) & -2048i64;
  930.  
  931.     if (fLimitTo4Gb && maxpoint >= 0x100000000i64) {
  932.         _RPT1(0,"overflow detected!  maxpoint=%I64d\n", maxpoint);
  933.         _RPT2(0,"lIndexSize = %08lx (%ld index blocks)\n", lIndexSize, idxblocks);
  934.         _RPT2(0,"sample counts = %ld, %ld\n", lIndexedChunkCount[0], lIndexedChunkCount[1]);
  935.  
  936.         throw MyError("Out of file space: Files cannot exceed 4 gigabytes on a FAT32 partition.");
  937.     }
  938.  
  939.     if (!_extendFile(maxpoint))
  940.         throw MyError("Not enough disk space to write additional data.");
  941.  
  942.     i64LastIndexedChunk[nStream] = chunkloc;
  943.  
  944.     // If we need to open a new Xblock, do so.
  945.  
  946.     if (fOpenNewBlock) {
  947.         _closeXblock();
  948.         _openXblock();
  949.     }
  950.  
  951.     // Write the chunk.
  952.  
  953.     avie.ckid    = ckid;
  954.     avie.pos    = i64FilePosition - (avi_movi_pos[0]+8); //chunkMisc.dwDataOffset - 2064;
  955.     avie.size    = cbBuffer;
  956.  
  957.     if (dwIndexFlags & AVIIF_KEYFRAME)
  958.         avie.size |= 0x80000000L;
  959.  
  960.     buf[0] = ckid;
  961.     buf[1] = cbBuffer;
  962.  
  963.     _write(buf, 8);
  964.  
  965.     i64XBufferLevel += siz;
  966.  
  967.     // ActiveMovie/WMP requires a non-zero dwSuggestedBufferSize for
  968.     // hierarchial indexing (piece of sh*t player).  So we continually
  969.     // bump it up to the largest chunk size;
  970.  
  971.  
  972.     if ((unsigned short)ckid == '10') {
  973.         if (cbBuffer > audioOut->streamInfo.dwSuggestedBufferSize)
  974.             audioOut->streamInfo.dwSuggestedBufferSize = cbBuffer;
  975.  
  976.         if (fExtendedAVI)
  977.             if (!index_audio->add(&avie)) throw MyError("%s error: couldn't add audio chunk to index",szME);
  978.     } else {
  979.         if (cbBuffer > videoOut->streamInfo.dwSuggestedBufferSize)
  980.             videoOut->streamInfo.dwSuggestedBufferSize = cbBuffer;
  981.  
  982.         if (fExtendedAVI)
  983.             if (!index_video->add(&avie)) throw MyError("%s error: couldn't add video chunk to index",szME);
  984.     }
  985.  
  986.     if (index)
  987.         if (!index->add(&avie)) throw MyError("%s error: couldn't add chunk to index",szME);
  988.  
  989.     _write(lpBuffer, cbBuffer);
  990.  
  991.     // Align to 8-byte boundary, not 2-byte, in capture mode.
  992.  
  993.     if (fCaptureMode) {
  994.         char *pp;
  995.         int offset = (cbBuffer + iPadOffset) & 7;
  996.  
  997.         // offset=0:    no action
  998.         // offset=1/2:    [00] 'JUNK' 6 <6 bytes>
  999.         // offset=3/4:    [00] 'JUNK' 4 <4 bytes>
  1000.         // offset=5/6:    [00] 'JUNK' 2 <2 bytes>
  1001.         // offset=7:    00
  1002.  
  1003.         if (offset) {
  1004.             buf[0]    = 0;
  1005.             buf[1]    = 'KNUJ';
  1006.             buf[2]    = (-offset) & 6;
  1007.             buf[3]    = 0;
  1008.             buf[4]    = 0;
  1009.  
  1010.             pp = (char *)&buf[1];
  1011.  
  1012.             if (offset & 1)
  1013.                 --pp;
  1014.  
  1015.             _write(pp, (offset & 1) + (((offset+1)&7) ? 8+buf[2] : 0));
  1016.         }
  1017.  
  1018.         iPadOffset = 0;
  1019.  
  1020.     } else {
  1021.  
  1022.         // Standard AVI: use 2 bytes
  1023.  
  1024.         if (cbBuffer & 1)
  1025.             _write(&zero, 1);
  1026.     }
  1027.  
  1028. }
  1029.  
  1030. void AVIOutputFile::_closeXblock() {
  1031.     avi_movi_len[xblock] = i64FilePosition - (avi_movi_pos[xblock]+8);
  1032.  
  1033.     if (!xblock) {
  1034.         avihdr.dwTotalFrames = videoOut->lTotalSamplesWritten;
  1035.         _writeLegacyIndex(true);
  1036.     }
  1037.  
  1038.     avi_riff_len[xblock] = i64FilePosition - (avi_riff_pos[xblock]+8);
  1039.  
  1040.     ++xblock;
  1041.  
  1042.     i64XBufferLevel = 0;
  1043. }
  1044.  
  1045. void AVIOutputFile::_openXblock() {
  1046.     DWORD dw[8];
  1047.  
  1048.     if (xblock >= MAX_AVIBLOCKS)
  1049.         throw MyError("%s: Exceeded maximum RIFF count (%d)", szME, MAX_AVIBLOCKS);
  1050.  
  1051.     // If we're in capture mode, keep this stuff aligned to 8-byte boundaries!
  1052.  
  1053.     if (xblock != 0) {
  1054.  
  1055.         avi_riff_pos[xblock] = i64FilePosition;
  1056.  
  1057.         dw[0] = FOURCC_RIFF;
  1058.         dw[1] = 0x7F000000;
  1059.         dw[2] = xblock ? 'XIVA' : ' IVA';
  1060.         dw[3] = FOURCC_LIST;
  1061.         dw[4] = 0x7F000000;
  1062.         dw[5] = 'ivom';    // movi
  1063.         _write(dw,24);
  1064.  
  1065.         avi_movi_pos[xblock] = i64FilePosition - 12;
  1066.     } else {
  1067.         avi_riff_pos[xblock] = 0;
  1068.  
  1069.         avi_movi_pos[xblock] = i64FilePosition;
  1070.  
  1071.         dw[0] = FOURCC_LIST;
  1072.         dw[1] = 0x7FFFFFFF;
  1073.         dw[2] = 'ivom';        // movi
  1074.  
  1075.         if (fCaptureMode)
  1076.             iPadOffset = 4;
  1077.  
  1078.         _write(dw, 12);
  1079.     }
  1080.  
  1081.     // WARNING: For AVIFile to parse the index correctly, it assumes that the
  1082.     // first chunk in an index starts right after the movi chunk!
  1083.  
  1084. //    dw[0] = ckidAVIPADDING;
  1085. //    dw[1] = 4;
  1086. //    dw[2] = 0;
  1087. //    _write(dw, 12);
  1088. }
  1089.  
  1090. void AVIOutputFile::_writeLegacyIndex(bool use_fastIO) {
  1091.     if (!index)
  1092.         return;
  1093.  
  1094.     if (!index->makeIndex())
  1095.         throw MyMemoryError();
  1096.  
  1097. //    if (use_fastIO && fastIO) {
  1098.         DWORD dw[2];
  1099.  
  1100.         dw[0] = ckidAVINEWINDEX;
  1101.         dw[1] = index->indexLen() * sizeof(AVIINDEXENTRY);
  1102.         _write(dw, 8);
  1103.         _write(index->indexPtr(), index->indexLen() * sizeof(AVIINDEXENTRY));
  1104. //    } else {
  1105. //        _writeHdrChunk(ckidAVINEWINDEX, index->indexPtr(), index->indexLen() * sizeof(AVIINDEXENTRY));
  1106.  
  1107.     delete index;
  1108.     index = NULL;
  1109. }
  1110.  
  1111. void AVIOutputFile::_createNewIndices(AVIIndex *index, AVISUPERINDEX *asi, _avisuperindex_entry *asie, bool is_audio) {
  1112.     AVIIndexEntry2 *asie2;
  1113.     int size;
  1114.     int actual;
  1115.     int indexnum=0;
  1116.     int blocksize;
  1117.  
  1118.     if (!index || !index->size())
  1119.         return;
  1120.  
  1121.     if (!index->makeIndex2())
  1122.         throw MyMemoryError();
  1123.  
  1124.     size = index->indexLen();
  1125.     asie2 = index->index2Ptr();
  1126.  
  1127.     memset(asie, 0, sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES);
  1128.  
  1129.     // Now we run into a bit of a problem.  DirectShow's AVI2 parser requires
  1130.     // that all index blocks have the same # of entries (except the last),
  1131.     // which is a problem since we also have to guarantee that each block
  1132.     // has offsets <4Gb.
  1133.  
  1134.     // For now, use a O(n^2) algorithm to find the optimal size.  This isn't
  1135.     // really a problem because this routine won't ever be called in time
  1136.     // critical circumstances, like streaming video capture.
  1137.  
  1138.     blocksize = MAX_INDEX_ENTRIES;
  1139.  
  1140.     {
  1141.         while(blocksize > 1) {
  1142.             int i;
  1143.             int nextblock = 0;
  1144.             __int64 offset;
  1145.  
  1146.             for(i=0; i<size; i++) {
  1147.                 if (i == nextblock) {
  1148.                     nextblock += blocksize;
  1149.                     offset = asie2[i].pos;
  1150.                 }
  1151.  
  1152.                 if (asie2[i].pos >= offset + 0x100000000i64)
  1153.                     break;
  1154.             }
  1155.  
  1156.             if (i >= size)
  1157.                 break;
  1158.  
  1159.             --blocksize;
  1160.         }
  1161.     }
  1162.  
  1163.     // Write out the actual index blocks.
  1164.  
  1165.     while(size > 0) {
  1166.         if (indexnum >= MAX_SUPERINDEX_ENTRIES)
  1167.             throw MyError("Maximum number of extended AVI indices exceeded (%d)\n", MAX_SUPERINDEX_ENTRIES);
  1168.  
  1169.         actual = _writeNewIndex(&asie[indexnum], asie2, min(size, blocksize),
  1170.             is_audio ? '10xi' : '00xi', is_audio ? 'bw10' : videoOut->id, is_audio ? audioOut->streamInfo.dwSampleSize : videoOut->streamInfo.dwSampleSize);
  1171.  
  1172.         asie2 += actual;
  1173.         size -= actual;
  1174.         ++indexnum;
  1175.     }
  1176.  
  1177.     memset(asi, 0, sizeof(AVISUPERINDEX));
  1178.     asi->fcc            = 'xdni';
  1179.     asi->cb                = sizeof(AVISUPERINDEX)-8 + sizeof(_avisuperindex_entry)*MAX_SUPERINDEX_ENTRIES;
  1180.     asi->wLongsPerEntry    = 4;
  1181.     asi->bIndexSubType    = 0;
  1182.     asi->bIndexType        = AVI_INDEX_OF_INDEXES;
  1183.     asi->nEntriesInUse    = indexnum;
  1184.     asi->dwChunkId        = is_audio ? 'bw10' : videoOut->id;
  1185. }
  1186.  
  1187. int AVIOutputFile::_writeNewIndex(struct _avisuperindex_entry *asie, AVIIndexEntry2 *avie2, int size, FOURCC fcc, DWORD dwChunkId, DWORD dwSampleSize) {
  1188.     AVISTDINDEX asi;
  1189.     AVIIndexEntry3 asie3[64];
  1190.     __int64 offset = avie2->pos;
  1191.     int tc;
  1192.     int i;
  1193.     int size0;
  1194.  
  1195.     // Scan, ascertain how many we can handle without exceeding 4Gb offset
  1196.  
  1197.     for(i=0; i<size; i++) {
  1198.         if (avie2[i].pos - offset >= 0x100000000i64)
  1199.             break;
  1200.     }
  1201.  
  1202.     size0 = size = i;
  1203.  
  1204.     // Check to see if we need to open a new AVIX block
  1205.  
  1206.     if (i64XBufferLevel + sizeof(AVISTDINDEX) + size*sizeof(_avistdindex_entry) > (xblock ? 0x7F000000 : lAVILimit)) {
  1207.         _closeXblock();
  1208.         _openXblock();
  1209.     }
  1210.  
  1211.     // setup superindex entry
  1212.  
  1213.     asie->qwOffset    = i64FilePosition;
  1214.     asie->dwSize    = sizeof(AVISTDINDEX) + size*sizeof(_avistdindex_entry);
  1215.  
  1216.     if (dwSampleSize) {
  1217.         __int64 total_bytes = 0;
  1218.  
  1219.         for(int i=0; i<size; i++)
  1220.             total_bytes += avie2->size & 0x7FFFFFFF;
  1221.  
  1222.         asie->dwDuration = (DWORD)(total_bytes / audioOut->streamInfo.dwSampleSize);
  1223.     } else
  1224.         asie->dwDuration = size;
  1225.  
  1226.     asi.fcc                = ((dwChunkId & 0xFFFF)<<16) | 'xi';
  1227.     asi.cb                = asie->dwSize - 8;
  1228.     asi.wLongsPerEntry    = 2;
  1229.     asi.bIndexSubType    = 0;
  1230.     asi.bIndexType        = AVI_INDEX_OF_CHUNKS;
  1231.     asi.nEntriesInUse    = size;
  1232.     asi.dwChunkId        = dwChunkId;
  1233.     asi.qwBaseOffset    = offset;
  1234.     asi.dwReserved3        = 0;
  1235.  
  1236.     _write(&asi, sizeof asi);
  1237.  
  1238.     while(size > 0) {
  1239.         tc = size;
  1240.         if (tc>64) tc=64;
  1241.  
  1242.         for(i=0; i<tc; i++) {
  1243.             asie3[i].dwOffset    = (DWORD)(avie2->pos - offset + avi_movi_pos[0] + 16);
  1244.             asie3[i].dwSizeKeyframe        = avie2->size;
  1245.             ++avie2;
  1246.         }
  1247.  
  1248.         _write(asie3, tc*sizeof(AVIIndexEntry3));
  1249.  
  1250.         size -= tc;
  1251.     }
  1252.  
  1253.     return size0;
  1254. }
  1255.